#include "fire_sfx.h"

#include "num/rand.h"

#include "graphics/graphics_util.h"
#include "script/scripthelper.h"
#include "script/write_lua.h"
#include "font/font_manager.h"
#include "dd/dd_man.h"
#include "util/mmgr.h"		    // Needs to come in early
#include "util/common_macros.h"		// macros
#include "util/log.h"		    // logging
#include "util/timer.h"		    // logging
#include <cassert>

using namespace vfx;


void	CyclicFX::UseFireRamp()
{
	m_Colors.Reset();
	m_Colors.AddColor(1, 1, 1, 1, 0);	// starts white hot
	m_Colors.AddColor(1, 1, 0.3f, 1, 0.3f);	// shifts to yellow
	m_Colors.AddColor(1, 0.7f, 0.3f, 0.7f, 0.5f);	// then orance
	m_Colors.AddColor(0, 0, 0, 0, 1);	// then fades out towards a red color
	m_Colors.Recalc();	// force a recalculation
}
void	CyclicFX::UseSmokeRamp()
{
	m_Colors.Reset();
	m_Colors.AddColor(0.4f, 1, 0.4f, 1, 0);	// starts white 
	m_Colors.AddColor(0.4f, 0.5f, 0.4f, 1, 0.75f);	// fades over time	
	m_Colors.AddColor(0, 0.3f, 0, 0, 1);	// then fades out towards a green color
	m_Colors.Recalc();	// force a recalculation

}
void	CyclicFX::UseWaterRamp()
{
	m_Colors.Reset();
	m_Colors.AddColor(1, 1, 1, 1, 0);
	m_Colors.AddColor(0.5f, 0.5f, 1, 1, 0.75f);	
	m_Colors.AddColor(0, 0, 1, 0, 1);	
	m_Colors.Recalc();	
}
void	CyclicFX::UsePoisonRamp()
{
	m_Colors.Reset();
	m_Colors.AddColor(0, 1, 0, 1, 0);
	m_Colors.AddColor(0.5f, 1.0, 0.5f, 0.75, 0.75f);	
	m_Colors.AddColor(0, 0, 1, 0.25f, 0.9f );	
	m_Colors.AddColor(0, 0, 0, 0, 1);	
	m_Colors.Recalc();	
}

CyclicFX::CyclicFX(int MaxC) :
	m_MaxC(MaxC),
	m_LastObj(0),
	m_MaxTime(1.0f),
	m_Duration(1),
	m_LastGen(0),
	m_pCBData(0),
	m_pCB(0),
	m_ActiveC(0),
	m_Gravity(0),
	m_FuzzyBirth(0)

{
	m_Colors.AddColor(1, 1, 1, 1, 0);
	m_Colors.AddColor(1, 1, 1, 1, 1);
	m_Colors.Recalc();	

	m_ParticleA = new CyclicPart[m_MaxC];
	memset(m_ParticleA, 0, sizeof(CyclicPart) * m_MaxC);
}

CyclicFX::~CyclicFX()
{
	UTIL_SAFE_DELETEA(m_ParticleA);
}

void	CyclicFX::Init(const std::string& Sprite)
{
	m_Sprite.Create(Sprite);
	m_Sprite.GetScreenSize(m_SpriteSize);
}

void	CyclicFX::Shutdown()
{

}

void	CyclicFX::ForceGen()
{
	CyclicPart* pObj = &m_ParticleA[m_LastObj];
	m_LastObj = (m_LastObj + 1) % m_MaxC;
	if (m_pCB)
	{
		m_pCB(pObj, m_pCBData);
	}
}

void	CyclicFX::GenContinuous(float dt)
{
	float	GenTime = m_MaxTime / (float)m_MaxC;
	m_LastGen+=dt;
	while (m_LastGen > GenTime)
	{
	
		CyclicPart* pObj = &m_ParticleA[m_LastObj];
		m_LastObj = (m_LastObj + 1) % m_MaxC;
		if (m_pCB)
		{
			m_pCB(pObj, m_pCBData);
		}
		m_LastGen-= GenTime;
	}
}

void	CyclicFX::Tick(float dt)
{
	// update current oness
	float	Time = TIMER().Time();
	m_ActiveC = 0;
	for (int i=0; i<m_MaxC; i++)
	{
		CyclicPart* pObj = &m_ParticleA[i];
		float	t = 1.0f - ((Time-pObj->SpawnTime) / pObj->Duration);
		if (t < 0) continue;	// expired	
		m_ActiveC++;
		pObj->Speed.y   +=(m_Gravity*dt);
		pObj->Pos		+=pObj->Speed * dt;
		pObj->Rotation  += pObj->RotSpeed * dt;
	}
}

void	CyclicFX::Render(const math::Vec2& Offset, float Scale)
{
	float	Time = TIMER().Time();
	Graphics::Vertex2D*	VertA = Graphics::GetTempVert();		// rewrite it to support the indices instead?
	int	Index = 0;
	math::Vec2	UV1, UV2;
	UV1 = m_Sprite.GetUV1();
	UV2 = m_Sprite.GetUV2();
	ui::SpriteInstance*	pSprite = m_Sprite.GetSpriteInst();
	//GetGraphics() ->SetupAdditiveAlpha(true);
	for (int i=0; i<m_MaxC; i++)
	{
		CyclicPart* pObj = &m_ParticleA[i];
		float	t = 1.0f - ((Time-pObj->SpawnTime) / pObj->Duration);
		if (t < 0) continue;	// expired
		COLOR	c = m_Colors.GetColor(1-t);	
		// collapse this into a single draw buffer?
		
		math::Vec2	P = (m_Pos + pObj->Pos - Offset)*Scale;
		math::Vec2	Size = pObj->Size * Scale * m_SpriteSize;
	
		if (pSprite)
		{
			pSprite->GetUV(Time-pObj->SpawnTime, UV1, UV2);
		}
		Index+=GraphicsUtil::FillRotQuad(P, Size, pObj->Rotation, UV1, UV2, c, &VertA[Index]);
			
		if (Index +12 >= Graphics::MAX_TEMP_VERT)
		{
			m_Sprite.SetTexture();
			GetGraphics()->DrawVertex2D(VertA, Index);
			Index = 0;
		}
	}
	if (Index>= 0)
	{
		m_Sprite.SetTexture();
		GetGraphics()->DrawVertex2D(VertA, Index);
	}
	//GetGraphics()->SetupAdditiveAlpha(false);
}

void	CyclicFX::RenderDebug(const math::Vec2& Offset, float Scale)
{
}


void	CyclicFX::GenBasicUpCB(CyclicFX::CyclicPart* pObj, void* pData)
{
	CyclicFX* pSFX = (CyclicFX*)pData;
	num::RAND32& rnd = num::RAND32::Instance();

	pObj->Pos = pSFX->GetPos() + rnd.RandVec2(-pSFX->m_FuzzyBirth, pSFX->m_FuzzyBirth);
	pObj->Rotation = rnd.RandFloat(0, 360);
	pObj->RotSpeed = rnd.RandFloat(-360, 360);
	pObj->Size	= rnd.RandFloat(0.5f, 1.2f);
	pObj->SpawnTime = TIMER().Time();
	pObj->Speed.x = 0.0f;
	pObj->Speed.y = rnd.RandFloat(-0.2f, -1.0f);
	pObj->Duration = pSFX->m_Duration;
}

void	CyclicGravFX::Tick(float dt)
{
	float	Time = TIMER().Time();
	m_ActiveC = 0;
	for (int i=0; i<m_MaxC; i++)
	{
		CyclicPart* pObj = &m_ParticleA[i];
		float	t = 1.0f - ((Time-pObj->SpawnTime) / pObj->Duration);
		if (t < 0) continue;	// expired	
		m_ActiveC++;
		pObj->Speed.x   += m_Gravity.x * dt;
		pObj->Speed.y   += m_Gravity.y * dt;
		pObj->Pos		+=pObj->Speed * dt;
		if (m_HasFloor && pObj->Pos.y>m_Floor)
		{
			pObj->Pos.y = m_Floor;
			pObj->Speed.y = -0.1f;

//			pObj->Speed.y = -0.1f;
///			if (pObj->Speed.x > 0) pObj->Speed.x = 0.4f; else pObj->Speed.x = -0.4f;
		}
		pObj->Rotation  += pObj->RotSpeed * dt;
	}

}
